import { Extension } from "@tiptap/core";
import { AllSelection, TextSelection } from "@tiptap/pm/state";

declare module "@tiptap/core" {
  interface Commands<ReturnType> {
    setLineHeight: {
      setLineHeight: (lineHeight: any) => ReturnType;
    };
    unsetLineHeight: {
      unsetLineHeight: () => ReturnType;
    };
  }
}

export default Extension.create({
  name: "lineHeight",

  addOptions() {
    return {
      types: ["paragraph", "heading"],
      defaultLineHeight: 1.5,
    };
  },

  addGlobalAttributes() {
    return [
      {
        types: this.options.types,
        attributes: {
          lineHeight: {
            default: this.options.defaultLineHeight,
            parseHTML: (element) =>
              element.style.lineHeight || this.options.defaultLineHeight,
            renderHTML: (attributes) => {
              if (attributes.lineHeight === this.options.defaultLineHeight) {
                return {};
              }
              return { style: `line-height: ${attributes.lineHeight}` };
            },
          },
        },
      },
    ];
  },

  addCommands() {
    return {
      setLineHeight:
        (lineHeight) =>
        ({ tr, state, dispatch }) => {
          const { selection } = state;
          const { from, to } = selection;
          const nodes: Array<{ node: any; pos: number }> = [];

          // 收集所有需要更新的节点（支持全选和部分选中）
          if (
            selection instanceof TextSelection ||
            selection instanceof AllSelection
          ) {
            state.doc.nodesBetween(from, to, (node, pos) => {
              if (this.options.types.includes(node.type.name)) {
                nodes.push({ node, pos });
                return false; // 不遍历子节点
              }
              return true;
            });
          } else {
            // 如果没有选中内容，获取当前节点
            const { $from } = selection;
            const node = $from.parent;
            if (node && this.options.types.includes(node.type.name)) {
              const pos = $from.before($from.depth);
              nodes.push({ node, pos });
            }
          }

          // 按位置倒序处理，避免位置偏移
          nodes.reverse().forEach(({ node, pos }) => {
            if (node.attrs.lineHeight !== lineHeight) {
              tr.setNodeMarkup(
                pos,
                node.type,
                {
                  ...node.attrs,
                  lineHeight,
                },
                node.marks
              );
            }
          });

          if (tr.docChanged && dispatch) {
            dispatch(tr);
            return true;
          }

          return false;
        },
      unsetLineHeight:
        () =>
        ({ tr, state, dispatch }) => {
          const { selection } = state;
          const { from, to } = selection;
          const nodes: Array<{ node: any; pos: number }> = [];

          if (
            selection instanceof TextSelection ||
            selection instanceof AllSelection
          ) {
            state.doc.nodesBetween(from, to, (node, pos) => {
              if (this.options.types.includes(node.type.name)) {
                nodes.push({ node, pos });
                return false;
              }
              return true;
            });
          } else {
            const { $from } = selection;
            const node = $from.parent;
            if (node && this.options.types.includes(node.type.name)) {
              const pos = $from.before($from.depth);
              nodes.push({ node, pos });
            }
          }

          nodes.reverse().forEach(({ node, pos }) => {
            if (node.attrs.lineHeight !== undefined) {
              const attrs = { ...node.attrs };
              delete attrs.lineHeight;
              tr.setNodeMarkup(pos, node.type, attrs, node.marks);
            }
          });

          if (tr.docChanged && dispatch) {
            dispatch(tr);
            return true;
          }

          return false;
        },
    };
  },
});

